/*
 * I2C client/driver for the Maxim/Dallas DS2782 Stand-Alone Fuel Gauge IC
 *
 * Copyright (C) 2009 Bluewater Systems Ltd
 *
 * Author: Ryan Mallon <ryan@bluewatersys.com>
 *
 * DS2786 added by Yulia Vilensky <vilensky@compulab.co.il>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/swab.h>
#include <linux/i2c.h>
#include <linux/idr.h>
#include <linux/power_supply.h>
#include <linux/slab.h>
#include <linux/ds2782_battery.h>

#define DS2782_REG_AS		0x14
#define DS2782_REG_ACR		0x10


#define DS2782_REG_RARC		0x06	/* Remaining active relative capacity */

#define DS278x_REG_VOLT_MSB	0x0c
#define DS278x_REG_TEMP_MSB	0x0a
#define DS278x_REG_CURRENT_MSB	0x0e

/* EEPROM Block */
#define DS2782_REG_RSNSP	0x69	/* Sense resistor value */

/* Current unit measurement in uA for a 1 milli-ohm sense resistor */
#define DS2782_CURRENT_UNITS	1563

#define DS2786_REG_RARC		0x02	/* Remaining active relative capacity */

#define DS2786_CURRENT_UNITS	25

struct ds278x_info;

struct ds278x_battery_ops {
    int (*get_battery_current)(struct ds278x_info *info, int *current_uA);
    int (*get_battery_voltage)(struct ds278x_info *info, int *voltage_uA);
    int (*get_battery_capacity)(struct ds278x_info *info, int *capacity_uA);
    int (*init_reg)(struct ds278x_info *info);
};

#define to_ds278x_info(x) container_of(x, struct ds278x_info, battery)

struct ds278x_info {
    struct i2c_client	*client;
    struct power_supply	battery;
    struct ds278x_battery_ops  *ops;
    int			id;
    int                     rsns;
};

static DEFINE_IDR(battery_id);
static DEFINE_MUTEX(battery_lock);

static inline int ds278x_write_byte(struct ds278x_info *info, int reg, u8 val) {
    unsigned char tbuf[2] = {reg, val};
    struct i2c_client *client = info->client;
    int k = 0;

    struct i2c_msg msg[] = {
        { client->addr, 0, sizeof(tbuf), tbuf },	/* read status + date */
    };

    int count = sizeof(msg) / sizeof(struct i2c_msg);
    /* read registers */
    if ((i2c_transfer(client->adapter, msg, count)) != count) {
        dev_err(&client->dev, "%s error\n", __func__);
        return -EIO;
    }
    return 0;
}
static inline int ds278x_write_byte16(struct ds278x_info *info, int reg, u16 val) {
    unsigned char tbuf[3] = {reg, val >> 8, val & 0xff};
    struct i2c_client *client = info->client;
    int k = 0;

    struct i2c_msg msg[] = {
        { client->addr, 0, sizeof(tbuf), tbuf },	/* read status + date */
    };

    int count = sizeof(msg) / sizeof(struct i2c_msg);
    /* read registers */
    if ((i2c_transfer(client->adapter, msg, count)) != count) {
        dev_err(&client->dev, "%s error\n", __func__);
        return -EIO;
    }
    return 0;
}
static inline int ds278x_read_reg(struct ds278x_info *info, int reg, u8 *val) {
    /*
    int ret;

    ret = i2c_smbus_read_byte_data(info->client, reg);
    if (ret < 0) {
    	dev_err(&info->client->dev, "register read failed\n");
    	return ret;
    }

    *val = ret;
    */
    // reg = 0x01;
    // printk("line %d reg 0x%x\r\n", __LINE__, reg);
    unsigned char rbuf[1] = {0};
    unsigned char tbuf[1] = {reg};
    struct i2c_client *client = info->client;
    int k = 0;
    // printk("addr %x\r\n", client->addr);
    struct i2c_msg msg[] = {
        { client->addr, 0, sizeof(tbuf), tbuf },	/* read status + date */
        { client->addr, I2C_M_RD, sizeof(rbuf), rbuf },	/* read status + date */
        //		{0x34, I2C_M_RD, sizeof(rbuf), rbuf },	/* read status + date */
    };

    int count = sizeof(msg) / sizeof(struct i2c_msg);
    /* read registers */
    if ((i2c_transfer(client->adapter, msg, count)) != count) {
        dev_err(&client->dev, "%s: read error\n", __func__);
        return -EIO;
    }
    int i;
    //printk("page 0x%2.2x(%d)\r\n", page, addr_in_page);
    // for (i = 0; i < sizeof(rbuf); i++) {
    // 	printk("%2.2x ", rbuf[i]);
    // }
    // printk("\r\n");
    *val = rbuf[0];
    return 0;
}

static inline int ds278x_read_reg16(struct ds278x_info *info, int reg_msb,
                                    s16 *val) {
    // printk("line %d 0x[%x]\r\n", __LINE__, reg_msb);
    int ret;
    char bL, bH;
    ds278x_read_reg(info, reg_msb, &bH);
    ds278x_read_reg(info, reg_msb + 1, &bL);
    //	ret = swab16(i2c_smbus_read_word_data(info->client, reg_msb));
    ret = (bH << 8) | bL;
    if (ret < 0) {
        dev_err(&info->client->dev, "register read failed\n");
        return ret;
    }
    // printk("ret = %x\r\n", ret);
    *val = ret;
    return 0;
}

static int ds278x_get_temp(struct ds278x_info *info, int *temp) {
	unsigned char tbuf[1] = {0x08};
    unsigned char rbuf[2] = {0};
    struct i2c_client *client = info->client;

    struct i2c_msg msg[] = {
        { client->addr, 0, sizeof(tbuf), tbuf },	/* read status + date */
        { client->addr, I2C_M_RD, sizeof(rbuf), rbuf },	/* read status + date */
    };
    int count = sizeof(msg) / sizeof(struct i2c_msg);
    int ret;
    /* read registers */
    ret = i2c_transfer(client->adapter, msg, count);
    if (ret != count) {
        dev_err(&client->dev, "%s: read error\n", __func__);
    }
    unsigned int val =  rbuf[1] << 8 | rbuf[0];

	// 温度摄取小数点后，摄氏度
    *temp = val >> 8;

    return 0;
}
void change(struct ds278x_info *info) {

    // ds278x_write_byte(info, 0x64, 188);  //  VCHG
    // ds278x_write_byte(info, 0x65, 120);  //  IMIN
    // ds278x_write_byte(info, 0x66, 158);  // VAE
    // ds278x_write_byte(info, 0x67, 57);   // IAE


    // ds278x_write_byte(info, 0x10, 0x08);   // ACR
    // ds278x_write_byte(info, 0x11, 0x11);   // ACR

    // ds278x_write_byte(info, 0x10, 0x30);   // ACR
    // ds278x_write_byte(info, 0x11, 0x20);   // ACR
    ///////////////
    ds278x_write_byte(info, 0x61, 0x04);
    ds278x_write_byte(info, 0x62, 0x30);
    ds278x_write_byte(info, 0x63, 0x20);
    ds278x_write_byte(info, 0x64, 0xc3);
    ds278x_write_byte(info, 0x65, 0xa0);
    ds278x_write_byte(info, 0x66, 0xb8);
    ds278x_write_byte(info, 0x67, 0x32);
    ds278x_write_byte(info, 0x68, 0x01);
    ds278x_write_byte(info, 0x69, 0x32);
    ds278x_write_byte(info, 0x6a, 0x30);
    ds278x_write_byte(info, 0x6b, 0x20);
    ds278x_write_byte(info, 0x6c, 0x08);
    ds278x_write_byte(info, 0x6d, 0x0c);
    ds278x_write_byte(info, 0x6e, 0x08);
    ds278x_write_byte(info, 0x6f, 0x0c);
    ds278x_write_byte(info, 0x6c, 0x11);
    ds278x_write_byte(info, 0x6d, 0x22);
    ds278x_write_byte(info, 0x6e, 0x37);
    ds278x_write_byte(info, 0x6f, 0x7b);
    ds278x_write_byte(info, 0x74, 0x00);
    ds278x_write_byte(info, 0x75, 0x01);
    ds278x_write_byte(info, 0x76, 0x02);
    ds278x_write_byte(info, 0x77, 0x07);
    ds278x_write_byte(info, 0x78, 0x04);
    ds278x_write_byte(info, 0x79, 0x1e);
    ds278x_write_byte(info, 0x7a, 0x00);
}
static int ds2782_init_reg(struct ds278x_info *info) {
    int err;
    int i;

#define RESET_REG (0)
#define CONFIG_REG (0)

#if RESET_REG
    printk("clear\r\n");
    ds278x_write_byte(info, DS2782_REG_AS, 0x60);
    ds278x_write_byte16(info, 0x10, 0xffff);   // ACR
#endif
    // for (i = 0x61; i < 0x7f; i++) {
    // 	err = ds278x_write_byte(info, i, 0);
    // }
#if CONFIG_REG
    change(info);
#endif
    return 0;



    // err = ds278x_write_byte(info, DS2782_REG_AS, 0x80);
    // err = ds278x_write_byte16(info, DS2782_REG_ACR, 0x6201);
    err = ds278x_write_byte16(info, DS2782_REG_ACR, 0);
    // err = ds278x_write_byte16(info, DS2782_REG_ACR, 0x0010);
    // err = ds278x_write_byte16(info, DS2782_REG_ACR + 1, 0x01);



    return 0;
    err = ds278x_write_byte(info, DS2782_REG_RSNSP, 0x32);

    err = ds278x_write_byte(info, 0x61, 0x04);
    err = ds278x_write_byte(info, 0x62, 0x0c);
    err = ds278x_write_byte(info, 0x63, 0x84);
    err = ds278x_write_byte(info, 0x64, 0xd5);
    err = ds278x_write_byte(info, 0x65, 0x20);

    err = ds278x_write_byte(info, 0x66, 0xbd);// VAE
    err = ds278x_write_byte(info, 0x67, 0x18);// IAE

    err = ds278x_write_byte(info, 0x68, 0x06);
    err = ds278x_write_byte(info, 0x69, 0x32);
    // err = ds278x_write_byte(info, 0x69, 20);



    ////////////////////////////
    // return 0;
    err = ds278x_write_byte(info, 0x66, 200);
    err = ds278x_write_byte(info, 0x67, 160);

    err = ds278x_write_byte(info, 0x64, 0x7f);//  VCHG
    err = ds278x_write_byte(info, 0x65, 0x02);//  IMIN
    ////////////////////////////

    // FULL 40
    err = ds278x_write_byte(info, 0x6a, 0x0c);
    err = ds278x_write_byte(info, 0x6b, 0xb0);
    // FULL
    err = ds278x_write_byte(info, 0x6c, 0x10);
    err = ds278x_write_byte(info, 0x6d, 0x17);
    err = ds278x_write_byte(info, 0x6e, 0x3c);
    err = ds278x_write_byte(info, 0x6f, 0x49);

    // AE
    err = ds278x_write_byte(info, 0x70, 0x06);
    err = ds278x_write_byte(info, 0x71, 0x0d);
    err = ds278x_write_byte(info, 0x72, 0x15);
    err = ds278x_write_byte(info, 0x73, 0x2f);

    // SE
    err = ds278x_write_byte(info, 0x74, 0x03);
    err = ds278x_write_byte(info, 0x75, 0x05);
    err = ds278x_write_byte(info, 0x76, 0x08);
    err = ds278x_write_byte(info, 0x77, 0x1b);


    err = ds278x_write_byte(info, 0x78, 0x04);
    err = ds278x_write_byte(info, 0x79, 0x1f);

    err = ds278x_write_byte(info, 0x74, 0x00);
    change(info);

}
static int ds2786_init_reg(struct ds278x_info *info) {
    printk("未实现\r\n");
}
static int max1720x_get_current(struct ds278x_info *info, int *current_uA) {
    unsigned char tbuf[1] = {0x0a};
    unsigned char rbuf[2] = {0};
    struct i2c_client *client = info->client;

    struct i2c_msg msg[] = {
        { client->addr, 0, sizeof(tbuf), tbuf },	/* read status + date */
        { client->addr, I2C_M_RD, sizeof(rbuf), rbuf },	/* read status + date */
    };
    int count = sizeof(msg) / sizeof(struct i2c_msg);
    int ret;
    /* read registers */
    ret = i2c_transfer(client->adapter, msg, count);
    if (ret != count) {
        dev_err(&client->dev, "%s: read error\n", __func__);
    }
    short val =  rbuf[1] << 8 | rbuf[0];
    // printk("curr %d\r\n", val * 15625 / 100);
    *current_uA = val * 15625 / 100;

    return 0;
}


const int  MAX1720X_ADDR			= 0x36;//FuelGauge m5 addr need to write message.
const char MAX1720X_STATUS_ADDR		= 0x00; // Contains alert status and chip status
const char MAX1720X_VCELL_ADDR		= 0x09; // Lowest cell voltage of a pack, or the cell voltage for a single cell
const char MAX1720X_REPSOC_ADDR		= 0x06; // Reported state of charge
const char MAX1720X_REPCAP_ADDR		= 0x05; // Reported remaining capacity
const char MAX1720X_TEMP_ADDR		= 0x08; // Temperature
const char MAX1720X_CURENT_ADDR		= 0x0A; // Battery current
const char MAX1720X_TTE_ADDR		= 0x11; // Time to empty
const char MAX1720X_TTF_ADDR		= 0x20; // Time to full
const char MAX1720X_CAPACITY_ADDR	= 0x10; // Full capacity estimation
const char MAX1720X_VBAT_ADDR		= 0xDA; // Battery pack voltage
const char MAX1720X_AVCELL_ADDR		= 0x17; // Battery cycles
const char MAX1720X_COMMAND_ADDR	= 0x60; // Command register
const char MAX1720X_CONFIG2_ADDR	= 0xbb; // Command register
const char MAX1720X_QH				= 0x4d; // register displays the raw coulomb count generated by the device in mah
const char MAX1720X_Timer			= 0x3E; // The Timer register LSb is equal to 175.8ms giving a full-scale range of 0 to 3.2 hours.
const char MAX1720X_TimerH			= 0xBE; // An LSb of 3.2 hours gives a full-scale range for the register of up to 23.94 years.
const char MAX1720X_Shadow_ROM_addr = 0x0B; // Shadow ROM addr need to write message to NonVolatile memory. Addr changes 180h–1FFh->80h–FFh. Example,0x019D->0x009D(0x9D)
unsigned short _read_16(struct i2c_client *client, int reg) {
    unsigned char tbuf[1] = {reg};
    unsigned char rbuf[2] = {0};
    struct i2c_msg msg[] = {
        { client->addr, 0, sizeof(tbuf), tbuf },	/* read status + date */
        { client->addr, I2C_M_RD, sizeof(rbuf), rbuf },	/* read status + date */
    };
    int count = sizeof(msg) / sizeof(struct i2c_msg);
    int ret;

    ret = i2c_transfer(client->adapter, msg, count);
    if (ret != count) {
        dev_err(&client->dev, "%s: read error\n", __func__);
        return -EIO;
    }
    return (rbuf[1] << 8) | rbuf[0];
}
int getVoltage(struct i2c_client *client) {
    uint16_t voltage0 = _read_16(client, MAX1720X_VCELL_ADDR);
    // int voltage = 0.078125*voltage0;
    // printk("vol %f\r\n", voltage);
    return voltage0;
    // return voltage;											//return voltage; // //calculate actual value and return in mV
}
int getCurrent(struct i2c_client *client) {
    // max1720x::write(dev_ADDR, MAX1720X_CURENT_ADDR);
    // int current1 = (_read_16(client, MAX1720X_CURENT_ADDR)*0.0015625)*2;
    int current1 = _read_16(client, MAX1720X_CURENT_ADDR);
    // return -1;
    return current1;											//return current;//calculate actual value as 0.0015625 mV/Ohm/  Rsense(0.5=500mOhm)
}
static int max1720x_get_reg(struct ds278x_info *info, int *voltage_mV) {
    s16 raw;
    int err;

    /*
     * Voltage is measured in units of 4.88mV. The voltage is stored as
     * a 10-bit number plus sign, in the upper bits of a 16-bit register

     */

    int i;
    char regs[] = {
        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0f,
        0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1f,
        0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2f,
        0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3f,
        0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4f,
        0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbf,
        0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xdf
    };
    // 0x80,0x81,0x82,0x83,0x84,0x85};

    for (i = 0 ; i < sizeof(regs) / sizeof(char); i++) {
        printk("reg = %2.2x :", regs[i]);
        unsigned char tbuf[1] = {i};
        unsigned char rbuf[8] = {0};

        struct i2c_client *client = info->client;
        int k = 0;
        // printk("addr %x\r\n", client->addr);
        struct i2c_msg msg[] = {
            { client->addr, 0, sizeof(tbuf), tbuf },	/* read status + date */
            { client->addr, I2C_M_RD, sizeof(rbuf), rbuf },	/* read status + date */
            //		{0x34, I2C_M_RD, sizeof(rbuf), rbuf },	/* read status + date */
        };
        int count = sizeof(msg) / sizeof(struct i2c_msg);
        int ret;
        /* read registers */
        ret = i2c_transfer(client->adapter, msg, count);
        if (ret != count) {
            dev_err(&client->dev, "%s: read error\n", __func__);
            // return -EIO;
        }
        int i;
        //printk("page 0x%2.2x(%d)\r\n", page, addr_in_page);
        for (i = 0; i < sizeof(rbuf); i++) {
            printk("%2.2x ", rbuf[i]);
        }
        printk("\r\n");
    }
    struct i2c_client *client = info->client;

    printk("Vol %d, cur %d\r\n", (int)getVoltage(client), (short)getCurrent(client));
    // printk("\r\n");
    // *val = rbuf[0];

    // err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, &raw);
    // if (err) {
    // 	return err;
    // }
    // // *voltage_uA = (raw / 32) * 4800;
    // *voltage_mV = (raw / 32) * 488 / 100;
    return 0;
}
static int max1720x_get_voltage(struct ds278x_info *info, int *voltage_mV) {
    unsigned char tbuf[1] = {0x09};
    unsigned char rbuf[2] = {0};
    struct i2c_client *client = info->client;

    struct i2c_msg msg[] = {
        { client->addr, 0, sizeof(tbuf), tbuf },	/* read status + date */
        { client->addr, I2C_M_RD, sizeof(rbuf), rbuf },	/* read status + date */
    };
    int count = sizeof(msg) / sizeof(struct i2c_msg);
    int ret;
    /* read registers */
    ret = i2c_transfer(client->adapter, msg, count);
    if (ret != count) {
        dev_err(&client->dev, "%s: read error\n", __func__);
    }
    unsigned int val =  rbuf[1] << 8 | rbuf[0];

	// 单位mV
    *voltage_mV = val * 78125 / 1000000;
    return 0;
}
static int max1720x_get_capacity(struct ds278x_info *info, int *capacity) {

    unsigned char tbuf[1] = {0x06};
    unsigned char rbuf[2] = {0};
    struct i2c_client *client = info->client;

    struct i2c_msg msg[] = {
        { client->addr, 0, sizeof(tbuf), tbuf },	/* read status + date */
        { client->addr, I2C_M_RD, sizeof(rbuf), rbuf },	/* read status + date */
    };
    int count = sizeof(msg) / sizeof(struct i2c_msg);
    int ret;
    /* read registers */
    ret = i2c_transfer(client->adapter, msg, count);
    if (ret != count) {
        dev_err(&client->dev, "%s: read error\n", __func__);
    }
    int val =  rbuf[1] << 8 | rbuf[0];

    // 单位百分比，精度1/256，摄取小数点后
    // val / 256，
    *capacity = val >> 8;
    return 0;
}

static int ds2786_get_current(struct ds278x_info *info, int *current_uA) {
    unsigned char tbuf[1] = {0x06};
    unsigned char rbuf[2] = {0};
    struct i2c_client *client = info->client;

    struct i2c_msg msg[] = {
        { client->addr, 0, sizeof(tbuf), tbuf },	/* read status + date */
        { client->addr, I2C_M_RD, sizeof(rbuf), rbuf },	/* read status + date */
    };
    int count = sizeof(msg) / sizeof(struct i2c_msg);
    int ret;
    /* read registers */
    ret = i2c_transfer(client->adapter, msg, count);
    if (ret != count) {
        dev_err(&client->dev, "%s: read error\n", __func__);
    }
    short val =  rbuf[1] << 8 | rbuf[0];
    printk("curr %d\r\n", val);

    // 单位uA，摄取小数点后
    *current_uA = val;
    return 0;
}

static int ds2786_get_voltage(struct ds278x_info *info, int *voltage_uA) {
    s16 raw;
    int err;

    /*
     * Voltage is measured in units of 1.22mV. The voltage is stored as
     * a 10-bit number plus sign, in the upper bits of a 16-bit register
     */
    err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, &raw);
    if (err) {
        return err;
    }
    *voltage_uA = (raw / 8) * 1220;
    return 0;
}

static int ds2786_get_capacity(struct ds278x_info *info, int *capacity) {
    int err;
    u8 raw;

    err = ds278x_read_reg(info, DS2786_REG_RARC, &raw);
    if (err) {
        return err;
    }
    /* Relative capacity is displayed with resolution 0.5 % */
    *capacity = raw / 2 ;
    return 0;
}

static int ds278x_get_status(struct ds278x_info *info, int *status) {
    int err;
    int current_uA;
    int capacity;

    err = info->ops->get_battery_current(info, &current_uA);
    if (err) {
        return err;
    }

    err = info->ops->get_battery_capacity(info, &capacity);
    if (err) {
        return err;
    }

    if (capacity == 100) {
        *status = POWER_SUPPLY_STATUS_FULL;
    } else if (current_uA == 0) {
        *status = POWER_SUPPLY_STATUS_NOT_CHARGING;
    } else if (current_uA < 0) {
        *status = POWER_SUPPLY_STATUS_DISCHARGING;
    } else {
        *status = POWER_SUPPLY_STATUS_CHARGING;
    }

    return 0;
}

static int ds278x_battery_get_property(struct power_supply *psy,
                                       enum power_supply_property prop,
                                       union power_supply_propval *val) {
    struct ds278x_info *info = to_ds278x_info(psy);
    int ret;

    switch (prop) {
    case POWER_SUPPLY_PROP_STATUS:
        ret = ds278x_get_status(info, &val->intval);
        break;

    case POWER_SUPPLY_PROP_CAPACITY:
        ret = info->ops->get_battery_capacity(info, &val->intval);
        break;

    case POWER_SUPPLY_PROP_VOLTAGE_NOW:
        ret = info->ops->get_battery_voltage(info, &val->intval);
        break;

    case POWER_SUPPLY_PROP_CURRENT_NOW:
        ret = info->ops->get_battery_current(info, &val->intval);
        break;

    case POWER_SUPPLY_PROP_TEMP:
        ret = ds278x_get_temp(info, &val->intval);
        break;

    default:
        ret = -EINVAL;
    }

    return ret;
}

static enum power_supply_property ds278x_battery_props[] = {
    POWER_SUPPLY_PROP_STATUS,
    POWER_SUPPLY_PROP_CAPACITY,
    POWER_SUPPLY_PROP_VOLTAGE_NOW,
    POWER_SUPPLY_PROP_CURRENT_NOW,
    POWER_SUPPLY_PROP_TEMP,
};

static void ds278x_power_supply_init(struct power_supply *battery) {
    battery->type			= POWER_SUPPLY_TYPE_BATTERY;
    battery->properties		= ds278x_battery_props;
    battery->num_properties		= ARRAY_SIZE(ds278x_battery_props);
    battery->get_property		= ds278x_battery_get_property;
    battery->external_power_changed	= NULL;
}

static int max1720x_battery_remove(struct i2c_client *client) {
    struct ds278x_info *info = i2c_get_clientdata(client);

    power_supply_unregister(&info->battery);
    kfree(info->battery.name);

    mutex_lock(&battery_lock);
    idr_remove(&battery_id, info->id);
    mutex_unlock(&battery_lock);

    kfree(info);
    return 0;
}

enum ds278x_num_id {
    DS2782 = 0,
    DS2786,
};

static struct ds278x_battery_ops ds278x_ops[] = {
    [DS2782] = {
        .get_battery_current  = max1720x_get_current,
        .get_battery_voltage  = max1720x_get_voltage,
        .get_battery_capacity = max1720x_get_capacity,
        .init_reg            = ds2782_init_reg,
    },
    [DS2786] = {
        .get_battery_current  = ds2786_get_current,
        .get_battery_voltage  = ds2786_get_voltage,
        .get_battery_capacity = ds2786_get_capacity,
        .init_reg            = ds2786_init_reg,
    }
};

static int max1720x_battery_probe(struct i2c_client *client,
                                  const struct i2c_device_id *id) {
    struct ds278x_platform_data *pdata = client->dev.platform_data;
    struct ds278x_info *info;
    int ret;
    int num;
    /*
     * ds2786 should have the sense resistor value set
     * in the platform data
     */
    if (id->driver_data == DS2786 && !pdata) {
        dev_err(&client->dev, "missing platform data for ds2786\n");
        return -EINVAL;
    }

    /* Get an ID for this battery */
    ret = idr_pre_get(&battery_id, GFP_KERNEL);
    if (ret == 0) {
        ret = -ENOMEM;
        goto fail_id;
    }

    mutex_lock(&battery_lock);
    ret = idr_get_new(&battery_id, client, &num);
    mutex_unlock(&battery_lock);
    if (ret < 0) {
        goto fail_id;
    }

    info = kzalloc(sizeof(*info), GFP_KERNEL);
    if (!info) {
        ret = -ENOMEM;
        goto fail_info;
    }

    info->battery.name = kasprintf(GFP_KERNEL, "%s-%d", client->name, num);
    if (!info->battery.name) {
        ret = -ENOMEM;
        goto fail_name;
    }

    if (id->driver_data == DS2786) {
        info->rsns = pdata->rsns;
    }

    i2c_set_clientdata(client, info);
    info->client = client;
    info->id = num;
    info->ops  = &ds278x_ops[id->driver_data];
    ds278x_ops[id->driver_data].init_reg(info);
    ds278x_power_supply_init(&info->battery);

    ret = power_supply_register(&client->dev, &info->battery);
    if (ret) {
        dev_err(&client->dev, "failed to register battery\n");
        goto fail_register;
    }

    return 0;

fail_register:
    kfree(info->battery.name);
fail_name:
    kfree(info);
fail_info:
    mutex_lock(&battery_lock);
    idr_remove(&battery_id, num);
    mutex_unlock(&battery_lock);
fail_id:
    return ret;
}

static const struct i2c_device_id max1720x_id[] = {
    {"max1720x-6c", 0},
    // {"max1720x-16", 0},

    // {"max1720x", 0},
    // {"max1720x-16", 0},
    {},
};

static struct i2c_driver ds278x_battery_driver = {
    .driver 	= {
        .name	= "max1720x-battery",
    },
    .probe		= max1720x_battery_probe,
    .remove		= max1720x_battery_remove,
    .id_table	= max1720x_id,
};

static int __init max1720x_init(void) {
    return i2c_add_driver(&ds278x_battery_driver);
}
module_init(max1720x_init);

static void __exit max1720x_exit(void) {
    i2c_del_driver(&ds278x_battery_driver);
}
module_exit(max1720x_exit);

MODULE_AUTHOR("Ryan Mallon <ryan@bluewatersys.com>");
MODULE_DESCRIPTION("Maxim/Dallas DS2782 Stand-Alone Fuel Gauage IC driver");
MODULE_LICENSE("GPL");
